﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Skill.FbxSDK;

namespace SceneTreeView
{
    public class ImportExport
    {
        IEForm.UIOutput output = null;//to write messages
        public ImportExport(IEForm.UIOutput output)
        {
            this.output = output;
        }
        // to read and write a file using the FBXSDK readers/writers
        //
        // const char *ImportFileName : the full path of the file to be read
        // const char* ExportFileName : the full path of the file to be written
        // int pWriteFileFormat       : the specific file format number
        //                                  for the writer

        public void ImportAndExport(string importFileName, string exportFileName,
                                    int writeFileFormat, string passworld)
        {

            FbxSdkManager sdkManager;	// SdkManager pointer
            FbxScene scene;				// Scene pointer

            // Initialize the KFbxSdkManager and the KFbxScene
            if (!InitializeSdkObjects(out sdkManager, out scene))
                return;

            output.Print("------- Import started ---------------------------");

            // Load the scene.
            bool r = LoadScene(sdkManager, scene, importFileName, passworld);
            if (r)
                output.Print("------- Import succeeded -------------------------");
            else
            {
                output.Print("------- Import failed ----------------------------");

                DestroySdkObjects(sdkManager, scene);
                return;
            }

            output.Print("\r\n"); // add a blank line

            output.Print("------- Export started ---------------------------");

            // Save the scene.
            r = SaveScene(sdkManager,          // Use this manager...
                          scene,               // to export this scene...
                          exportFileName,       // to this path/filename...
                          writeFileFormat,     // using this file format.
                          false);               // Don't embed media files, if any.

            if (r) output.Print("------- Export succeeded -------------------------");
            else output.Print("------- Export failed ----------------------------");

            // Delete the FBX SDK manager.
            DestroySdkObjects(sdkManager, scene);
        }

        private bool InitializeSdkObjects(out FbxSdkManager sdkManager, out FbxScene scene)
        {
            // Create the FBX SDK memory manager object.
            // The SDK Manager allocates and frees memory
            // for almost all the classes in the SDK.
            sdkManager = FbxSdkManager.Create();

            if (sdkManager == null)
            {
                output.Print("Unable to create the FBX SDK manager");
                sdkManager = null;
                scene = null;
                return false;
            }

            // Create the scene object. The scene will hold the data
            // in the file to be imported.
            scene = FbxScene.Create(sdkManager, "");
            if (scene == null)
            {
                output.Print("Unable to create the Scene");
                sdkManager = null;
                scene = null;
                return false;
            }
            return true;
        }

        private void DestroySdkObjects(FbxSdkManager sdkManager, FbxScene scene)
        {
            // Delete the FBX SDK manager.
            // All the objects that
            // (1) have been allocated by the memory manager, AND that
            // (2) have not been explicitly destroyed
            // will be automatically destroyed.                        
            if (sdkManager != null)
                sdkManager.Destroy();

        }

        private bool LoadScene(FbxSdkManager sdkManager, FbxScene scene, string importFileName, string password)
        {
            Version fileVersion;
            Version sdkVersion;
            int fileFormat = -1;
            int i, takeCount;
            bool status = true;

            // Use memory manager to create an object
            // to store the options for the import of FBX files only.
            Skill.FbxSDK.IO.FbxStreamOptionsFbxReader importOptions =
                Skill.FbxSDK.IO.FbxStreamOptionsFbxReader.Create(sdkManager, "");

            // Get the version number of the FBX files generated by the
            // version of FBX SDK that you are using.
            sdkVersion = Skill.FbxSDK.IO.FbxIO.CurrentVersion;

            // Create an importer.
            Skill.FbxSDK.IO.FbxImporter importer = Skill.FbxSDK.IO.FbxImporter.Create(sdkManager, "");

            //Detect the file format of the file to be imported            
            if (!sdkManager.IOPluginRegistry.DetectFileFormat(importFileName, ref fileFormat))
            {
                // Unrecognizable file format.
                // Try to fall back to SDK's native file format (an FBX binary file).
                fileFormat =
                    sdkManager.IOPluginRegistry.NativeReaderFormat;
            }
            importer.FileFormat = fileFormat;

            // Initialize the importer by providing a filename.
            bool importStatus = importer.Initialize(importFileName);

            // Get the version number of the FBX file format.
            fileVersion = importer.FileVersion;

            if (!importStatus)  // Problem with the file to be imported
            {
                output.Print("Call to FbxImporter.Initialize() failed.");
                output.Print(string.Format("Error returned: {0}", importer.LastErrorString));

                if (importer.LastErrorID ==
                        Skill.FbxSDK.IO.FbxIO.Error.FileVersionNotSupportedYet ||
                    importer.LastErrorID ==
                        Skill.FbxSDK.IO.FbxIO.Error.FileVersionNotSupportedAnymore)
                {
                    output.Print(string.Format("FBX version number for this FBX SDK is {0}.{1}.{2}",
                               sdkVersion.Major, sdkVersion.Minor, sdkVersion.Revision));
                    output.Print(string.Format("FBX version number for file {0} is {1}.{2}.{3}",
                               importFileName, fileVersion.Major, fileVersion.Minor, fileVersion.Revision));
                }
                return false;
            }

            output.Print(string.Format("FBX version number for this FBX SDK is {0}.{1}.{2}",
                               sdkVersion.Major, sdkVersion.Minor, sdkVersion.Revision));

            if (importer.IsFBX)
            {
                output.Print(string.Format("FBX version number for file {0} is {1}.{2}.{3}",
                               importFileName, fileVersion.Major, fileVersion.Minor, fileVersion.Revision));

                // In FBX, a scene can have one or more "takes". A take is a
                // container for animation data.
                // You can access a file's take information without
                // the overhead of loading the entire file into the scene.

                output.Print("Take Information");

                takeCount = importer.TakeCount;

                output.Print(string.Format("    Number of takes: {0}", takeCount));
                output.Print(string.Format("    Current take: \"{0}\"",
                               importer.CurrentTakeName));

                for (i = 0; i < takeCount; i++)
                {
                    FbxTakeInfo takeInfo = importer.GetTakeInfo(i);

                    output.Print(string.Format("    Take {0}", i));
                    output.Print(string.Format("         Name: \"{0}\"", takeInfo.Name.ToString()));
                    output.Print(string.Format("         Description: \"{0}\"",
                                        takeInfo.Description.ToString()));

                    // Change the value of the import name if the take should
                    // be imported under a different name.
                    output.Print(string.Format("         Import Name: \"{0}\"", takeInfo.ImportName.ToString()));

                    // Set the value of the import state to false
                    // if the take should be not be imported.
                    output.Print(string.Format("         Import State: {0}", takeInfo.Select ? "true" : "false"));
                }

                // Import options determine what kind of data is to be imported.
                // The default is true, but here we set the options explictly.                

                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.MATERIAL, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.TEXTURE, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.LINK, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.SHAPE, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.GOBO, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.ANIMATION, true);
                importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.GLOBAL_SETTINGS, true);
            }

            // Import the scene.
            status = importer.Import(scene, importOptions);

            if (status == false &&     // The import file may have a password
               importer.LastErrorID == Skill.FbxSDK.IO.FbxIO.Error.PasswordError)
            {
                output.Print("Please enter password: ");

                if (password != null)
                {
                    importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.PASSWORD, password);
                    importOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.PASSWORD_ENABLE, true);
                    status = importer.Import(scene, importOptions);

                    if (status == false && importer.LastErrorID == Skill.FbxSDK.IO.FbxIO.Error.PasswordError)
                    {
                        output.Print("Incorrect password: file not imported.");
                    }
                }
            }

            // Destroy the importer.
            if (importOptions != null)
            {
                importOptions.Destroy();
                importOptions = null;
            }

            // Destroy the importer
            importer.Destroy();
            return status;
        }


        private bool SaveScene(FbxSdkManager sdkManager, FbxScene scene, string exportFileName, int writeFileFormat, bool embedMedia)
        {
            bool status = true;

            // Create an exporter.
            Skill.FbxSDK.IO.FbxExporter exporter = Skill.FbxSDK.IO.FbxExporter.Create(sdkManager, "");

            // Initialize the exporter by providing a filename.
            if (exporter.Initialize(exportFileName) == false)
            {
                output.Print("Call to FbxExporter.Initialize() failed.");
                output.Print(string.Format("Error returned: {0}", exporter.LastErrorString));
                return false;
            }

            Version version = Skill.FbxSDK.IO.FbxIO.CurrentVersion;
            output.Print(string.Format("FBX version number for this FBX SDK is {0}.{1}.{2}",
                      version.Major, version.Minor, version.Revision));


            if (writeFileFormat < 0 ||
                writeFileFormat >=
                    sdkManager.IOPluginRegistry.WriterFormatCount)
            {
                // Write in fall back format if pEmbedMedia is true
                writeFileFormat =
                    sdkManager.IOPluginRegistry.NativeWriterFormat;

                if (!embedMedia)
                {
                    //Try to export in ASCII if possible
                    int formatIndex, formatCount =
                        sdkManager.IOPluginRegistry.
                            WriterFormatCount;

                    for (formatIndex = 0; formatIndex < formatCount; formatIndex++)
                    {
                        if (sdkManager.IOPluginRegistry.
                            WriterIsFBX(formatIndex))
                        {
                            string desc = sdkManager.IOPluginRegistry.
                                GetWriterFormatDescription(formatIndex);
                            if (desc.Contains("ascii"))
                            {
                                writeFileFormat = formatIndex;
                                break;
                            }
                        }
                    }
                }
            }

            // Set the file format
            exporter.FileFormat = writeFileFormat;

            Skill.FbxSDK.IO.FbxStreamOptionsFbxWriter exportOptions =
                Skill.FbxSDK.IO.FbxStreamOptionsFbxWriter.Create(sdkManager, "");

            if (sdkManager.IOPluginRegistry.WriterIsFBX(writeFileFormat))
            {
                // Export options determine what kind of data is to be imported.
                // The default (except for the option eEXPORT_TEXTURE_AS_EMBEDDED)
                // is true, but here we set the options explictly.
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.MATERIAL, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.TEXTURE, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.EMBEDDED, embedMedia);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.LINK, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.SHAPE, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.GOBO, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.ANIMATION, true);
                exportOptions.SetOption(Skill.FbxSDK.IO.FbxStreamOptionsFbx.GLOBAL_SETTINGS, true);
            }

            // Export the scene.
            status = exporter.Export(scene, exportOptions);

            if (exportOptions != null)
            {
                exportOptions.Destroy();
                exportOptions = null;
            }

            // Destroy the exporter.
            exporter.Destroy();

            return status;
        }

    }
}
